package edu.neu.cs6515.player.strategy;

import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;

import edu.neu.cs6515.board.BoardCoord;
import edu.neu.cs6515.board.HexagonBoard;
import edu.neu.cs6515.board.ScoreCalc;
import edu.neu.cs6515.util.Pair;
import edu.neu.cs6515.xml.IngeniousColor;
import edu.neu.cs6515.xml.Placement;
import edu.neu.cs6515.xml.Score;
import edu.neu.cs6515.xml.Tile;

/**
 * Strategy #1: "Greedy Strategy"
 * 
 * ---------------------------------------------------------------------
 * 
 * - Enumerate all possible placements (use getCurrentBoardState() in proxy to
 * get the information about the already occupied hexagons) - For each placement
 * try each tile - Choose the placement to get the highest score for any of the
 * player's colors that has not exceeded 18 points yet
 */
public class GreedyStrategy extends AbstractStrategy {

    @Override
    public Placement getNextMove(final List<Tile> tilesInHand,
            final int numberOfPlayers, final List<Placement> placementsSofar,
            final Score scoreBeforePlacement) {

        HexagonBoard scoreTestBoard = new HexagonBoard(numberOfPlayers);
        List<Pair<BoardCoord>> possiblePlacements = getAllPossiblePlacements(
                placementsSofar, scoreTestBoard);
        if (possiblePlacements.size() == 0) {
            throw new StrategyException("no more openning on the board");
        }

        return findBestPlacement(tilesInHand, scoreBeforePlacement,
                scoreTestBoard, possiblePlacements);
    }

    /**
     * @param tilesInHand
     *            tiles currently in the hand of the player
     * @param scoreBeforePlacement
     *            Score of the player before this placement
     * @param scoreTestBoard
     *            the board to be used to calculate scores
     * @param possiblePlacements
     *            the list of possible, legal placements on the board
     * @return the best placement according to this strategy
     */
    private Placement findBestPlacement(final List<Tile> tilesInHand,
            final Score scoreBeforePlacement,
            final HexagonBoard scoreTestBoard,
            final List<Pair<BoardCoord>> possiblePlacements)
            throws StrategyException {

        Placement bestPlacementSoFar = createPlacement(
                possiblePlacements.get(0), tilesInHand.get(0));

        for (Pair<BoardCoord> pair : possiblePlacements) {
            for (Tile tile : tilesInHand) {
                Placement attemptedPlacement = createPlacement(pair, tile);
                if (currentIsBetterThenBestSoFar(scoreTestBoard,
                        attemptedPlacement, scoreBeforePlacement,
                        bestPlacementSoFar)) {
                    bestPlacementSoFar = attemptedPlacement;
                }
            }
        }
        return bestPlacementSoFar;
    }

    /**
     * @param scoreTestBoard
     *            the board to be used to calculate scores
     * @param attemptedPlacement
     *            the currently attempted placement
     * @param scoreBeforePlacement
     *            Score of the player before this placement
     * @param bestPlacementSoFar
     *            the highest scoring placement that has been found so far
     * @return true if the attempted placement is better then the bestPlacement
     *         so far
     */
    private boolean currentIsBetterThenBestSoFar(
            final HexagonBoard scoreTestBoard,
            final Placement attemptedPlacement,
            final Score scoreBeforePlacement, final Placement bestPlacementSoFar)
            throws StrategyException {

        Score scoreOfAttemptedPlacement = scoreTestBoard
                .scoreAMove(attemptedPlacement);
        Score scoreOfBestPlacementSoFar = scoreTestBoard
                .scoreAMove(bestPlacementSoFar);

        int highestColorScoreOfAttemptedPlacement = getHighestColorIncrease(
                scoreOfAttemptedPlacement, scoreBeforePlacement);
        int highestColorScoreSoFar = getHighestColorIncrease(
                scoreOfBestPlacementSoFar, scoreBeforePlacement);

        return highestColorScoreOfAttemptedPlacement > highestColorScoreSoFar;
    }

    /**
     * Returns the highest possible score increase for any color
     * 
     * @param scoreOfAttemptedPlacement
     *            the score gained by the attempted placement
     * @param scoreBeforePlacement
     *            Score of the player before this placement
     * @return integer with the highest increase possible (over all colors) by
     *         this placement
     */
    private int getHighestColorIncrease(final Score scoreOfAttemptedPlacement,
            final Score scoreBeforePlacement) throws StrategyException {
        List<Entry<IngeniousColor, Integer>> sortedScore = scoreOfAttemptedPlacement
                .getIncreasingOrderSortedScore();
        Collections.reverse(sortedScore);
        for (Entry<IngeniousColor, Integer> entry : sortedScore) {
            if (scoreBeforePlacement.getPointsForColor(entry.getKey()) < ScoreCalc.BONUS_SCORE_LINE) {
                return entry.getValue();
            }
        }
        return sortedScore.get(0).getValue();
    }
}
